using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Core.Event
{
    /// <summary>
    /// 自定义事件分发器
    /// </summary>
    public static class EventManager
    {
        // 两类事件监听表
        private static readonly Dictionary<Enum, Delegate> s_NoParamListeners = new();
        private static readonly Dictionary<(Enum evt, Type paramType), Delegate> s_Listeners = new();

        private static readonly Dictionary<Enum, List<Delegate>> s_NoParamOnceListeners = new();
        private static readonly Dictionary<(Enum evt, Type paramType), List<Delegate>> s_OnceListeners = new();
        private static readonly Queue<(Enum evt, object sender, object param)> s_Queue = new();

        /// <summary>
        /// 添加无参永久事件监听
        /// </summary>
        /// <param name="evt"></param>
        /// <param name="callback"></param>
        public static void AddListener(Enum evt, Action<object> callback)
        {

            if (s_NoParamListeners.TryGetValue(evt, out var existing))
                s_NoParamListeners[evt] = Delegate.Combine(existing, callback);
            else
                s_NoParamListeners[evt] = callback;
        }

        /// <summary>
        /// 添加永久事件监听
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="evt"></param>
        /// <param name="callback"></param>
        public static void AddListener<T>(Enum evt, Action<object, T> callback)
        {
            var key = (evt, typeof(T));
            if (s_Listeners.TryGetValue(key, out var existing))
                s_Listeners[key] = Delegate.Combine(existing, callback);
            else
                s_Listeners[key] = callback;
        }


        /// <summary>
        /// 添加一次性监听
        /// </summary>
        /// <param name="evt"></param>
        /// <param name="callback"></param>
        public static void ListenOnce(Enum evt, Action<object> callback)
        {
            if (!s_NoParamOnceListeners.ContainsKey(evt))
                s_NoParamOnceListeners[evt] = new List<Delegate>();

            s_NoParamOnceListeners[evt].Add(callback);
        }

        /// <summary>
        /// 添加一次性监听
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="evt"></param>
        /// <param name="callback"></param>
        public static void ListenOnce<T>(Enum evt, Action<object, T> callback)
        {
            var key = (evt, typeof(T));
            if (!s_OnceListeners.ContainsKey(key))
                s_OnceListeners[key] = new List<Delegate>();

            s_OnceListeners[key].Add(callback);
        }

        /// <summary>
        /// 移除监听
        /// </summary>
        /// <param name="evt"></param>
        /// <param name="callback"></param>
        public static void RemoveListener(Enum evt, Action<object> callback)
        {
            if (s_NoParamListeners.TryGetValue(evt, out var existing))
            {
                var newDel = Delegate.Remove(existing, callback);
                if (newDel == null)
                    s_NoParamListeners.Remove(evt);
                else
                    s_NoParamListeners[evt] = newDel;
            }

            if (s_NoParamOnceListeners.TryGetValue(evt, out var onceList))
            {
                onceList.Remove(callback);
                if (onceList.Count == 0)
                    s_NoParamOnceListeners.Remove(evt);
            }
        }


        /// <summary>
        ///  移除监听
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="evt"></param>
        /// <param name="callback"></param>
        public static void RemoveListener<T>(Enum evt, Action<object, T> callback)
        {
            var key = (evt, typeof(T));

            if (s_Listeners.TryGetValue(key, out var existing))
            {
                var newDel = Delegate.Remove(existing, callback);
                if (newDel == null)
                    s_Listeners.Remove(key);
                else
                    s_Listeners[key] = newDel;
            }

            if (s_OnceListeners.TryGetValue(key, out var onceList))
            {
                onceList.Remove(callback);
                if (onceList.Count == 0)
                    s_OnceListeners.Remove(key);
            }
        }

        /// <summary>
        /// 派发事件同步
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="evt"></param>
        /// <param name="sender"></param>
        /// <param name="param"></param>
        public static void Dispatch(Enum evt, object sender)
        {
            if (s_NoParamListeners.TryGetValue(evt, out var del) && del is Action<object> cb)
            {
                cb.Invoke(sender);
            }

            if (s_NoParamOnceListeners.TryGetValue(evt, out var onceList))
            {
                foreach (var one in onceList)
                    if (one is Action<object> onceCb)
                        onceCb.Invoke(sender);

                s_NoParamOnceListeners.Remove(evt);
            }
        }

        /// <summary>
        /// 派发事件
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="evt"></param>
        /// <param name="sender"></param>
        /// <param name="param"></param>
        public static void Dispatch<T>(Enum evt, object sender, T param = default)
        {
            var key = (evt, typeof(T));

            if (s_Listeners.TryGetValue(key, out var del) && del is Action<object, T> cb)
                cb.Invoke(sender, param);

            if (s_OnceListeners.TryGetValue(key, out var onceList))
            {
                foreach (var one in onceList)
                    if (one is Action<object, T> onceCb)
                        onceCb.Invoke(sender, param);

                s_OnceListeners.Remove(key);
            }
        }

        /// <summary>
        /// 派发事件（异步）
        /// </summary>
        /// <param name="evt"></param>
        /// <param name="sender"></param>
        /// <returns></returns>
        public static Task DispatchAsync(Enum evt, object sender)
        {
            return Task.Run(() => Dispatch(evt, sender));
        }

        /// <summary>
        /// 派发事件（异步）
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="evt"></param>
        /// <param name="sender"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        public static Task DispatchAsync<T>(Enum evt, object sender, T param)
        {
            return Task.Run(() => Dispatch(evt, sender, param));
        }

        /// <summary>
        /// 加入事件队列（延迟派发）
        /// </summary>
        /// <param name="evt"></param>
        /// <param name="sender"></param>
        public static void Enqueue(Enum evt, object sender)
        {
            s_Queue.Enqueue((evt, sender, null));
        }

        /// <summary>
        /// 加入事件队列（延迟派发）
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="evt"></param>
        /// <param name="sender"></param>
        /// <param name="param"></param>
        public static void Enqueue<T>(Enum evt, object sender, T param)
        {
            s_Queue.Enqueue((evt, sender, param));
        }

        /// <summary>
        /// 执行所有队列事件
        /// </summary>
        public static void FlushQueue()
        {
            while (s_Queue.Count > 0)
            {
                var (evt, sender, param) = s_Queue.Dequeue();
                if (param == null)
                {
                    Dispatch(evt, sender);
                }
                else
                {
                    var method = typeof(EventManager).GetMethod(nameof(Dispatch)).MakeGenericMethod(param.GetType());
                    method.Invoke(null, new object[] { evt, sender, param });
                }
            }
        }

        /// <summary>
        /// 移除所有事件监听
        /// </summary>
        public static void ClearAll()
        {
            s_Listeners.Clear();
            s_NoParamListeners.Clear();
            s_OnceListeners.Clear();
            s_NoParamOnceListeners.Clear();
            s_Queue.Clear();
        }
    }
}
